
#include "IWindow.h"

#include <cstdio>
#include <BCharEvent>
#include <BKeyEvent>
#include <BEvent>
#include <BEvent>
#include <BFocusEvent>
#include <BMouseEvent>
#include <BEvent>
#include <BCharEvent>
#include <BDragEvent>
#include <BTextEdit>
#include <BPainter>
#include <BCursor>
#include <BDrag>
#include <BSystem>
#include <BScreen>
#include "member_BWidget.h"
#include "member_BEvent.h"

using namespace BWE;

extern BList<IWindowHolder> app_windows;

#ifdef _WINDOWS_SYSTEM_

void Update_window(HWND hwnd, HDC hdc)
{
	RECT r;
	GetClientRect(hwnd, &r);

	HDC memdc = CreateCompatibleDC(hdc);
	HBITMAP bmp = CreateCompatibleBitmap(hdc, r.right - r.left, r.bottom - r.top);
	HBITMAP oldbmp = (HBITMAP)SelectObject(memdc,bmp);

	BLENDFUNCTION blend;
	blend.BlendOp = AC_SRC_OVER;
	blend.BlendFlags = 0;
	blend.SourceConstantAlpha = 255;
	blend.AlphaFormat = AC_SRC_ALPHA;

	POINT src = POINT(),dst;
	SIZE size;

	size.cx = r.right - r.left;
	size.cy = r.bottom - r.top;

	GetWindowRect(hwnd,& r);
	dst.x = r.left;
	dst.y = r.top;

	BOOL ret = UpdateLayeredWindow(hwnd, hdc, &dst, &size, memdc, &src, 0, &blend, ULW_ALPHA);

	SelectObject(memdc,oldbmp);
	DeleteObject(bmp);
	DeleteDC(memdc);
}
bool IWindow::create()
{
	hwnd = 0;
	hdc = 0;
	context = 0;
	style = WS_POPUP;//WS_BORDER,WS_POPUP,WS_OVERLAPPEDWINDOW
	mouseInside = false;

	const char* title = boss->title().cstr();

	const BRect& rect = boss->rect();
	int x = rect.x();
	int y = rect.y();
	int w = rect.width();
	int h = rect.height();

	const char* ClassName = "IWindow";

	HINSTANCE hInstance = GetModuleHandle(0);

	WNDCLASSEX wcex;
	memset(&wcex, 0, sizeof(wcex));
	wcex.cbSize = sizeof(wcex);
	wcex.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex.lpfnWndProc = WindowProc;
	wcex.hInstance = hInstance;
	wcex.hCursor = LoadCursor(0, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszClassName = ClassName;

	ATOM atom = RegisterClassEx(&wcex);
	
	hwnd = CreateWindow(ClassName, title, style, x, y, w, h, 0, 0, hInstance, 0);
	if (!hwnd)
	{
		perror("create hwnd failed.");
		return false;
	}

	DragAcceptFiles(hwnd, TRUE);

	SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
	BOOL ret0 = SetLayeredWindowAttributes(hwnd, 0, 255, LWA_COLORKEY | LWA_ALPHA);

	hdc = GetDC(hwnd);
	if (!hdc)
	{
		perror("Get hwnd device faild.");
		DestroyWindow(hwnd);
		hwnd = 0;
		return false;
	}

	context = CreateContext(hdc);
	if(!context)
	{
		perror("Create GL context faild.");
		DestroyWindow(hwnd);
		hwnd = 0;
		hdc = 0;
		return false;
	}
	
	BOOL ret = UpdateWindow(hwnd);
	showNormal();

	HIMC himc = ImmGetContext(hwnd);

	return true;
}
bool IWindow::destroy()
{
	if (context)
		wglDeleteContext(context);
	if (hwnd)
		DestroyWindow(hwnd);
	hwnd = 0;
	context = 0;
	return true;
}
bool IWindow::makeCurrent(bool yes)
{
	if (yes)
	{
		BOOL ret = wglMakeCurrent(hdc, context);
		if (ret == FALSE)
		{
			DWORD error = GetLastError();
			printf("Error : %d\n", error);
		}
	}
	else
	{
		BOOL ret = wglMakeCurrent(0, 0);

	}
	return true;
}
bool IWindow::swapBuffers()
{
	BOOL ret = SwapBuffers(hdc);
	//Update_window(hwnd, hdc);
	return ret;
}

bool IWindow::setWindowTitle(const BString& title)
{
	if (hwnd)
	{
		return SetWindowTextA(hwnd, title.cstr());
	}
	return false;
}

void IWindow::setWindowPos(const BPoint& pos)
{
	if (hwnd)
	{
		BSize size = this->getWindowSize();
		BOOL ret = ::MoveWindow(hwnd, pos.x(), pos.y(), size.width(), size.height(), TRUE);
	}
}
BPoint IWindow::getWindowPos()
{
	if (hwnd)
	{
		RECT windowRect;
		GetWindowRect(hwnd, &windowRect);
		return BPoint(windowRect.left, windowRect.top);
	}
	return boss->pos();
}

void IWindow::setWindowSize(const BSize& size)
{
	if (hwnd)
	{
		RECT windowRect;
		GetWindowRect(hwnd, &windowRect);
		int ww = windowRect.right - windowRect.left;
		int wh = windowRect.bottom - windowRect.top;

		BSize wsize = this->getWindowSize();
		if (wsize != size)
		{
			BOOL ret = MoveWindow(hwnd, windowRect.left, windowRect.top, size.width(), size.height(), TRUE);
		}
	}
}
BSize IWindow::getWindowSize()
{
	if (hwnd)
	{
		RECT clientRect;
		GetClientRect(hwnd, &clientRect);
		int w = clientRect.right - clientRect.left;
		int h = clientRect.bottom - clientRect.top;
		return BSize(w, h);
	}
	return boss->size();
}

void IWindow::showNormal()
{
	if (hwnd)
	{
		ShowWindow(hwnd, SW_SHOWNORMAL);
	}
}
void IWindow::showMaxinum()
{
	if (hwnd)
	{
		ShowWindow(hwnd, SW_SHOWMAXIMIZED);
	}
}
void IWindow::showMininum()
{
	if (hwnd)
	{
		ShowWindow(hwnd, SW_SHOWMINIMIZED);
	}
}
void IWindow::showFullScreen()
{
	if (hwnd)
	{
		ShowWindow(hwnd, SW_SHOWMAXIMIZED);
	}
}
void IWindow::hideWindow()
{
	if (hwnd)
	{
		ShowWindow(hwnd, SW_HIDE);
	}
}

void IWindow::freshCursor()
{
	BCursor* cursor = 0;
	if (hoverWidget)
		cursor = hoverWidget->cursor();
	if (cursor && !cursor->able())
		return;
	if (!cursor)
	{
		SetCursor(LoadCursor(0, IDC_ARROW));
		return;
	}
	CursorType type = cursor->type();
	switch (cursor->type())
	{
	case Cursor_Custom:
		BCursor::hide();
		break;
	case Cursor_Cross:
		SetCursor(LoadCursor(0, IDC_CROSS));
		break;
	case Cursor_Wait:
		SetCursor(LoadCursor(0, IDC_WAIT));
		puts("Cursor Wait");
		break;
	case Cursor_SizeVer:
		SetCursor(LoadCursor(0, IDC_SIZENS));
		break;
	case Cursor_SizeHor:
		SetCursor(LoadCursor(0, IDC_SIZEWE));
		break;
	case Cursor_SizeBiasLeft:
		SetCursor(LoadCursor(0, IDC_SIZENWSE));
		break;
	case Cursor_SizeBiasRight:
		SetCursor(LoadCursor(0, IDC_SIZENESW));
		break;
	case Cursor_SizeAll:
		SetCursor(LoadCursor(0, IDC_SIZEALL));
		break;
	case Cursor_Forbidden:
		SetCursor(LoadCursor(0, IDC_NO));
		break;
	default:
		SetCursor(LoadCursor(0, IDC_ARROW));
		break;
	}
}

Button filterButton(UINT message)
{
	switch(message)
	{
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_LBUTTONDBLCLK:
		return Button_Left;
		
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MBUTTONDBLCLK:
		return Button_Middle;
		
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_RBUTTONDBLCLK:
		return Button_Right;
		
	}
	return Button_None;
}

HGLRC BWE::CreateContext(HDC hdc)
{
	PIXELFORMATDESCRIPTOR pixelFormat;
	memset(&pixelFormat, 0, sizeof(pixelFormat));
	pixelFormat.nSize = sizeof(pixelFormat);
	pixelFormat.nVersion = 1;
	pixelFormat.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
	pixelFormat.iPixelType = PFD_TYPE_RGBA;
	pixelFormat.cColorBits = 32;
	pixelFormat.cDepthBits = 32;
	pixelFormat.iLayerType = PFD_MAIN_PLANE;

	int pixelFormatIndex = ChoosePixelFormat(hdc, &pixelFormat);
	if (pixelFormatIndex == 0)
		return 0;

	DescribePixelFormat(hdc, pixelFormatIndex, sizeof(PIXELFORMATDESCRIPTOR), &pixelFormat);

	int result = SetPixelFormat(hdc, pixelFormatIndex, &pixelFormat);
	if (!result)
		return 0;
	
	return wglCreateContext(hdc);
}

LRESULT CALLBACK BWE::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_IME_REQUEST)
	{
		int value = 0;
		switch (wParam)
		{
		case IMR_CANDIDATEWINDOW:
			value = 0;
			break;
		case IMR_COMPOSITIONFONT:
			value = 0;
			break;
		case IMR_COMPOSITIONWINDOW:
			value = 0;
			break;
		case IMR_CONFIRMRECONVERTSTRING:
			value = 0;
			break;
		case IMR_DOCUMENTFEED:
			value = 0;
			break;
		case IMR_QUERYCHARPOSITION:
		{
			IMECHARPOSITION* ptr = (IMECHARPOSITION*)lParam;
			ptr->pt.x = 500;
			ptr->pt.y = 500;
			break;
		}
		case IMR_RECONVERTSTRING:
			value = 0;
			break;
		}
	}

	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (!window->boss)
			continue;
		if (window->hwnd == hwnd)
		{
			window->procMessage(message, wParam, lParam);
			break;
		}
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
bool IWindow::procMessage(UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_IME_NOTIFY:
	{
		int a = 642;
		break;
	}
	case WM_IME_SELECT:
	{
		break;
	}
	case WM_IME_SETCONTEXT:
	{
		BOOL fSet = (BOOL)wParam;
		int iShow = (DWORD)lParam;
		break;
	}
	case WM_INPUTLANGCHANGE:
	{
		break;
	}
	case WM_IME_ENDCOMPOSITION:
	{
		break;
	}
	case WM_PAINT:
	{
		refresh = true;
		break;
	}
	case WM_CREATE:
	{
		refresh = true;
		break;
	}
	case WM_SETCURSOR:
	{
		break;
	}
	case WM_TIMER:
	{
		if (wParam)
		{
			KillTimer(hwnd, wParam);
		}
		break;
	}
	case WM_MOVE:
	{
		short x = LOWORD(lParam);
		short y = HIWORD(lParam);
		widget_member(boss)->syncWindowPos(BPoint(x, y));
		break;
	}
	case WM_SIZE:
	{
		int width = LOWORD(lParam);
		int height = HIWORD(lParam);
		widget_member(boss)->syncWindowSize(BSize(width, height));
		break;
	}
	case WM_SETFOCUS:
	{
		focusing = true;
		BFocusEvent focusEvent(Focus_In);
		widget_member(boss)->procEvent(focusEvent);
		break;
	}
	case WM_KILLFOCUS:
	{
		ReleaseCapture();
		focusing = false;
		BFocusEvent focusEvent(Focus_Out);
		widget_member(boss)->procEvent(focusEvent);
		break;
	}
	case WM_KEYDOWN:
	{
		if (focusWidget)
		{
			Key key = filterKey(wParam);
			BKeyEvent keyEvent(Event_KeyDown, key);
			widget_member(focusWidget)->procEvent(keyEvent);
		}
		break;
	}
	case WM_KEYUP:
	{
		if (focusWidget)
		{
			Key key = filterKey(wParam);
			BKeyEvent keyEvent(Event_KeyUp, key);
			widget_member(focusWidget)->procEvent(keyEvent);
			break;
		}
		break;
	}
	case WM_CHAR:
	{
		if (focusWidget)
		{
			short code = (short)LOWORD(wParam);
			short flag = (short)LOWORD(lParam);
			BCharEvent charEvent(code);
			widget_member(focusWidget)->procEvent(charEvent);
		}
		break;
	}
	case WM_IME_CHAR:
	{
		short code = (short)LOWORD(wParam);
		short flag = (short)LOWORD(lParam);
		BCharEvent charEvent(code);
		widget_member(focusWidget)->procEvent(charEvent);
		break;
	}
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	{
		BPoint absolutePos((short)LOWORD(lParam), (short)HIWORD(lParam));
		Button button = filterButton(message);
		
		BMouseEvent mouseEvent(Event_MousePress, button);
		mouseEvent.setAbsolutePos(absolutePos);
		this->procMousePress(mouseEvent);

		break;
	}
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	{
		BPoint absolutePos((short)LOWORD(lParam), (short)HIWORD(lParam));
		Button button = filterButton(message);
		
		BMouseEvent mouseEvent(Event_MouseRelease, button);
		mouseEvent.setAbsolutePos(absolutePos);
		this->procMouseRelease(mouseEvent);
		break;
	}
	case WM_CAPTURECHANGED:
	{
		break;
	}
	case WM_MOUSEMOVE:
	{
		if (!mouseInside)
		{
			mouseInside = true;
			mouse_pressed = false;
			BMouseEvent mouseEvent(Event_MouseEnter);
			widget_member(boss)->procEvent(mouseEvent);

			TRACKMOUSEEVENT eventTrack;
			eventTrack.cbSize = sizeof(TRACKMOUSEEVENT);
			eventTrack.dwFlags = TME_LEAVE | TME_HOVER;
			eventTrack.hwndTrack = hwnd;
			eventTrack.dwHoverTime = 0;
			TrackMouseEvent(&eventTrack);
		}
		BPoint absolutePos((short)LOWORD(lParam), (short)HIWORD(lParam));
		Button button = filterButton(message);

		BMouseEvent mouseEvent(Event_MouseMove, mouse_button);
		mouseEvent.setAbsolutePos(absolutePos);
		this->procMouseMove(mouseEvent);

		break;
	}
	case WM_MOUSEHOVER:
	{
		BPoint absolutePos((short)LOWORD(lParam), (short)HIWORD(lParam));
		//printf("mouse hover event : %d\t%d\n", absolutePos.x(), absolutePos.y());
		break;
	}
	case WM_MOUSELEAVE:
	{
		mouseInside = false;
		BMouseEvent mouseEvent(Event_MouseLeave);
		widget_member(boss)->procEvent(mouseEvent);
		break;
	}
	case WM_MOUSEWHEEL:
	{
		BPoint absolutePos((short)LOWORD(lParam), (short)HIWORD(lParam));
		int wheel = (short)HIWORD(wParam) / 120;
		
		BMouseEvent mouseEvent(Event_MouseWheel, Button_Middle);
		mouseEvent.setAbsolutePos(absolutePos);
		mouseEvent.setWheel(wheel);
		this->procMouseWheel(mouseEvent);
		break;
	}
	case WM_CLIPBOARDUPDATE:
	{
		break;
	}
	case WM_DROPFILES:
	{
		const char fileName[256] = {};
		HDROP hDrop = (HDROP)wParam;
		int count = DragQueryFile(hDrop, -1, 0, 0);
		if (count > 0)
		{
			BDrag* drag = new BDrag();
			BDragEvent dragEvent;
			dragEvent.setType(Event_DragDrop);
			dragEvent.setDrag(drag);
			BString text;
			for (int i = 0; i < count; i++)
			{
				DragQueryFile(hDrop, i, (LPSTR)fileName, 256);
				text += fileName;
				text += ';';
			}
			drag->setText(text);
			widget_member(hoverWidget)->procEvent(dragEvent);
		}
		DragFinish(hDrop);
		break;
	}
	default:
		return false;
	}
	return true;
}
Key IWindow::filterKey(WPARAM wParam)
{
	Key key = Key_None;
	switch (wParam)
	{
	case 0x30:	key = Key_0;	break;
	case 0x31:	key = Key_1;	break;
	case 0x32:	key = Key_2;	break;
	case 0x33:	key = Key_3;	break;
	case 0x34:	key = Key_4;	break;
	case 0x35:	key = Key_5;	break;
	case 0x36:	key = Key_6;	break;
	case 0x37:	key = Key_7;	break;
	case 0x38:	key = Key_8;	break;
	case 0x39:	key = Key_9;	break;

	case 0x41:	key = Key_A;	break;
	case 0x42:	key = Key_B;	break;
	case 0x43:	key = Key_C;	break;
	case 0x44:	key = Key_D;	break;
	case 0x45:	key = Key_E;	break;
	case 0x46:	key = Key_F;	break;
	case 0x47:	key = Key_G;	break;
	case 0x48:	key = Key_H;	break;
	case 0x49:	key = Key_I;	break;
	case 0x4A:	key = Key_J;	break;
	case 0x4B:	key = Key_K;	break;
	case 0x4C:	key = Key_L;	break;
	case 0x4D:	key = Key_M;	break;
	case 0x4E:	key = Key_N;	break;
	case 0x4F:	key = Key_O;	break;
	case 0x50:	key = Key_P;	break;
	case 0x51:	key = Key_Q;	break;
	case 0x52:	key = Key_R;	break;
	case 0x53:	key = Key_S;	break;
	case 0x54:	key = Key_T;	break;
	case 0x55:	key = Key_U;	break;
	case 0x56:	key = Key_V;	break;
	case 0x57:	key = Key_W;	break;
	case 0x58:	key = Key_X;	break;
	case 0x59:	key = Key_Y;	break;
	case 0x5A:	key = Key_Z;	break;

	case 0x25:	key = Key_Left;		break;
	case 0x26:	key = Key_Up;		break;
	case 0x27:	key = Key_Right;	break;
	case 0x28:	key = Key_Down;		break;

	case VK_END:	key = Key_End;		break;
	case VK_HOME:	key = Key_Home;		break;

	case VK_TAB:	key = Key_Tab;		break;
	case VK_SPACE:	key = Key_Space;	break;

	case VK_DELETE:	key = Key_Delete;	break;
	case VK_BACK:	key = Key_Back;		break;
	case VK_RETURN:	key = Key_Enter;	break;

	case VK_SHIFT:		key = Key_LShift;	break;
	case VK_LSHIFT:		key = Key_LShift;	break;
	case VK_RSHIFT:		key = Key_RShift;	break;
	case VK_CONTROL:	key = Key_LCtrl;	break;
	case VK_LCONTROL:	key = Key_LCtrl;	break;
	case VK_RCONTROL:	key = Key_RCtrl;	break;
	case VK_MENU:		key = Key_LAlt;		break;
	case VK_LMENU:		key = Key_LAlt;		break;
	case VK_RMENU:		key = Key_RAlt;		break;

	case 0x01:	key = Key_LButton;	break;
	case 0x02:	key = Key_RButton;	break;
	case 0x04:	key = Key_MButton;	break;

	case VK_ESCAPE:	key = Key_ESC;	break;

	default:
		break;
	}
	return key;
}

#endif
